/*****************************************************
 * Copyright ?, 2006-2008 Huawei Technologies Co.,Ltd. 
 * Filename: haldd.cpp
 * Version: 1.0 / Author: Lei Yu / Date: 2007-4-27
 * Description: 
 * Version: 
 * Function List :
 * History:
 * leiyu    2007-4-27   1.0  create this file
 ****************************************************/

#include "precomp.h"

/* if not a DDraw driver, then include dummy versions of these function */
#ifndef DD_ENABLE
void RegisterDDHALAPI(void)
{
    /* no DDHAL support */
    return;
}

void HALInit()
{
    DEBUGMSG(GPE_ZONE_HISI,(TEXT("HALInit: ERROR! This is a non-DirectDraw driver.\n")));
}

/* DD_ENABLE */
#else 

#ifdef DEBUG
#ifndef HAL_ZONE_INIT
#define HAL_ZONE_INIT     GPE_ZONE_INIT
#endif
#endif

extern DDHAL_DDMISCELLANEOUSCALLBACKS MiscellaneousCallbacks;

const struct {
    DWORD dwCode;
    DWORD dwBpp;
    DWORD dwYBitMask;
    DWORD dwUBitMask;
    DWORD dwVBitMask;
} adwDescOfFourCC[] =
{
    { FOURCC_YV12, 12, (DWORD) -1, (DWORD) -1, (DWORD) -1 },
    { FOURCC_NV12, 12, (DWORD) -1, (DWORD) -1, (DWORD) -1 },        
    { FOURCC_YUY2, 16, 0x00FF00FF, 0x0000FF00, 0xFF000000 },
    { FOURCC_UYVY, 16, 0xFF00FF00, 0x000000FF, 0x00FF0000 },
    { FOURCC_YVYU, 16, 0xFF00FF00, 0x000000FF, 0x00FF0000 },
};

#define NUMCODES_FOURCC  (sizeof(adwDescOfFourCC)/sizeof(adwDescOfFourCC[0]))

#define PITCH_DEFAULT  8

/****************************** externals ******************************/

EXTERN_C SCODE DDGPEGetPixelFormatFromSurfaceDesc ( LPDDSURFACEDESC         lpDDSurfaceDesc,
                                                    EDDGPEPixelFormat*      pPixelFormat,
                                                    EGPEFormat*             pFormat );


/****************************** prototypes ******************************/


static BOOL SurfaceAllocate(DDGPESurf                ** ppGPESurf,
                               DDRAWI_DDRAWSURFACE_LCL   * lpLcl,
                               DWORD                       dwPitchOverride,
                               LPDDSURFACEDESC             lpDDSurfaceDesc,
                               DWORD                       dwSizeOverride);

static BOOL SystemSurfaceAllocate(DDGPESurf                ** ppGPESurf,
                                       DDRAWI_DDRAWSURFACE_LCL   * lpLcl,
                                       LPDDSURFACEDESC             lpDDSurfaceDesc);

void CreateVideoSurface(DDGPESurf                ** ppGPESurf,
                            DDRAWI_DDRAWSURFACE_LCL   * lpLcl,
                            LPDDSURFACEDESC             lpDDSurfaceDesc,
                            DWORD                       dwPitchOverride,
                            BOOL                        blOverlayUse);

DWORD WINAPI HalGetDriverInfo(LPDDHAL_GETDRIVERINFODATA lpInput)
{
    DWORD dwSize;
    DEBUGMSG(GPE_ZONE_HISI,(TEXT("GetDriverInfo invoked !!\r\n")));

    lpInput->ddRVal = DDERR_CURRENTLYNOTAVAIL;

    if (IsEqualIID(lpInput->guidInfo, GUID_MiscellaneousCallbacks))
    {
        DEBUGMSG(GPE_ZONE_HISI,(TEXT("GUID_MiscellaneousCallbacks\r\n")));
        dwSize = min(lpInput->dwExpectedSize, sizeof(DDHAL_DDMISCELLANEOUSCALLBACKS));
        lpInput->dwActualSize = sizeof(DDHAL_DDMISCELLANEOUSCALLBACKS);
        memcpy(lpInput->lpvData, &MiscellaneousCallbacks, dwSize);
        lpInput->ddRVal = DD_OK;
    }

    if( lpInput->ddRVal != DD_OK )
    {
        DEBUGMSG(GPE_ZONE_HISI,(TEXT("HalGetDriverInfo: Currently not available\r\n")));
    }

    return DDHAL_DRIVER_HANDLED;
}

/*****************************************************
 * Function: HalWaitForVerticalBlank
 * Description: helps the application synchronize 
 *              itself with the vertical blanking interval.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: pd - Pointer to a DDHAL_WAITFORVERTICALBLANKDATA 
 *             structure that contains the vertical 
 *             blank information.
 * Output: NONE
 * Return: 
 * Others: NONE
 ****************************************************/
DWORD WINAPI HalWaitForVerticalBlank(LPDDHAL_WAITFORVERTICALBLANKDATA pd)
{
    DEBUGENTER(HalWaitForVerticalBlank);

    return ((HisiDisp *)GetGPE())->WaitForVerticalBlank(pd);
}

DWORD HisiDisp::WaitForVerticalBlank(LPDDHAL_WAITFORVERTICALBLANKDATA pd)
{
    switch (pd->dwFlags)
    {
    case DDWAITVB_I_TESTVB:
        pd->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;

    case DDWAITVB_BLOCKBEGIN:
        pd->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;

    case DDWAITVB_BLOCKEND:
        pd->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;

    default:
        pd->ddRVal = DDERR_INVALIDPARAMS;
        return DDHAL_DRIVER_NOTHANDLED;
    }
}

DWORD HisiDisp::GetScanLine()
{
    DWORD dwScanLine;

    dwScanLine = _memrD_EDCreg(EDC_STS);
    
    return ((dwScanLine & BAS_RD_LINE_MASK) >> BAS_RD_LINE_SHIFT);
}

/*****************************************************
 * Function: HalGetScanLine
 * Description: get the current scan line.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: pd - Pointer to a DDHAL_GETSCANLINEDATA 
 *             structure that contains the scan line 
 *             information.
 * Output: NONE
 * Return: 
 * Others: NONE
 ****************************************************/
DWORD WINAPI HalGetScanLine(LPDDHAL_GETSCANLINEDATA pd)
{
    DEBUGENTER(HalGetScanLine);

    pd->dwScanLine = ((HisiDisp *)GetGPE())->GetScanLine();
    pd->ddRVal = DD_OK;

    DEBUGLEAVE( HalGetScanLine );
    return DDHAL_DRIVER_HANDLED;
}

/*****************************************************
 * Function: ValidSurfaceData
 * Description: Check for valid Surface type (RGB,YUV) 
 *              and valid Pixel Depth (RGB) dependend 
 *              on the current chipset.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: blOverlayUse - whether to usr overlay or not
 * Output: pDdPixelFormat - pix format
 * Return: TRUE: valid format, FALSE: invalid format
 * Others: NONE
 ****************************************************/
BOOL ValidSurfaceData(DDPIXELFORMAT * pDdPixelFormat,
                          BOOL blOverlayUse)
{
    DWORD dwFourCC        = pDdPixelFormat->dwFourCC;
    DWORD dwPixelFormat   = pDdPixelFormat->dwFlags;
    DWORD dwPixelDepth    = pDdPixelFormat->dwRGBBitCount;

    if ((dwPixelFormat & DDPF_RGB)  &&
        (dwPixelDepth == 16 || dwPixelDepth == 32))
    {
        return TRUE;
    }
    else if (dwPixelFormat & DDPF_FOURCC)
    {
        DWORD i;

        for (i = 0; i < NUMCODES_FOURCC; i++)
        {
            if (dwFourCC == adwDescOfFourCC[i].dwCode)
            {
                pDdPixelFormat->dwYUVBitCount = adwDescOfFourCC[i].dwBpp;
                pDdPixelFormat->dwYBitMask    = adwDescOfFourCC[i].dwYBitMask;
                pDdPixelFormat->dwUBitMask    = adwDescOfFourCC[i].dwUBitMask;
                pDdPixelFormat->dwVBitMask    = adwDescOfFourCC[i].dwVBitMask;

                return TRUE;
            }
        }
    }
    
    return FALSE;
}

/*****************************************************
 * Function: HalCreateSurface
 * Description: creates a DirectDrawSurface object for 
 *              this DirectDraw object.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: pd - Pointer to a DDHAL_CREATESURFACEDATA 
 *             structure that the driver uses to create 
 *             the DirectDrawSurface object.
 * Output: NONE
 * Return: NONE
 * Others: NONE
 ****************************************************/
DWORD HalCreateSurface(LPDDHAL_CREATESURFACEDATA lpCreateSurface)
{
    DDRAWI_DDRAWSURFACE_LCL * lpSurfaceLocal;

    LPDDSURFACEDESC     lpSurfaceDesc;
    DWORD               i;
    DWORD               dwSurfCaps;
    DWORD               dwPitchAlignment = PITCH_DEFAULT; /* no special alignment */
    DDGPE *              pDDGPE = GetDDGPE();
    BOOL                   bPrimary = FALSE;
    SCODE                sc = S_OK;

    DEBUGENTER(HalCreateSurface);

    DEBUGMSG(GPE_ZONE_HISI, (TEXT(
             "Enter HalCreateSurface, %d surface(s) to be created.\r\n"),
             lpCreateSurface->dwSCnt));

    lpSurfaceLocal  = lpCreateSurface->lplpSList[0];
    dwSurfCaps      = lpSurfaceLocal->ddsCaps.dwCaps;

/* BEGIN: Added by leiyu, 2008/02/26   PN:AT4D01035 */
#if 0
    if (dwSurfCaps & DDSCAPS_OVERLAY)
    {
        switch (pDDGPE->DDGPEPrimarySurface()->Rotate())
        {
        case DMDO_90:
        case DMDO_270:
            DWORD dwTemp;
            dwTemp = lpCreateSurface->lpDDSurfaceDesc->dwWidth;
            lpCreateSurface->lpDDSurfaceDesc->dwWidth 
                = lpCreateSurface->lpDDSurfaceDesc->dwHeight;
            lpCreateSurface->lpDDSurfaceDesc->dwHeight = dwTemp;
            break;
        }
    }
#endif
/* END:   Added by leiyu, 2008/02/26 */
    
    for (i = 0; i < lpCreateSurface->dwSCnt; i++)
    {
        lpSurfaceLocal  = lpCreateSurface->lplpSList[i];
        lpSurfaceDesc   = lpCreateSurface->lpDDSurfaceDesc;

        dwSurfCaps      = lpSurfaceLocal->ddsCaps.dwCaps;

        /* Fail safe value(s), if we don't allocate the surface */

        lpSurfaceLocal->dwReserved1 = (ULONG_PTR) NULL;

        /* start checks for the specific surface types */

        if (dwSurfCaps & DDSCAPS_OVERLAY)
        {
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("Create FOURCC/RGB Overlay.\r\n")));

            /* supported overlay size limit to VGA */

            if (lpSurfaceDesc->dwWidth > MAX_OVERLAY_WIDTH || 
                lpSurfaceDesc->dwHeight > MAX_OVERLAY_HEIGHT)
            {
                goto CREATESURF_FAILTOOBIG;
            }

            CreateVideoSurface((DDGPESurf **)&lpSurfaceLocal->dwReserved1,
                                 lpSurfaceLocal,
                                 lpSurfaceDesc,
                                 dwPitchAlignment,
                                 TRUE);
        }
        else if (dwSurfCaps & DDSCAPS_PRIMARYSURFACE)
        {
            DEBUGMSG(GPE_ZONE_HISI, (TEXT( "HalCreateSurface Primary.\r\n")));
            
            if (FALSE == SurfaceAllocate((DDGPESurf **)&lpSurfaceLocal->dwReserved1, lpSurfaceLocal, 0, lpSurfaceDesc, 0))
            {
                DISPMSG(GPE_ZONE_HISI, (TEXT("Create Primary Surface failed.\r\n")));
            }
            bPrimary = TRUE;
        }
        else if (lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_FOURCC)
        {
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("Create DDPF_FOURCC Offscreen Video Surface.\r\n")));

            /* allocate the offscreen video surface data */

            CreateVideoSurface((DDGPESurf **)&lpSurfaceLocal->dwReserved1,
                                     lpSurfaceLocal,
                                     lpSurfaceDesc,
                                     dwPitchAlignment,
                                     FALSE);
        }
        else if (dwSurfCaps & DDSCAPS_VIDEOMEMORY)
        {
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("Create FOURCC/RGB Offscreen surface in Video Memory.\r\n")));

            /* allocate the surface data */
            SurfaceAllocate((DDGPESurf **)&lpSurfaceLocal->dwReserved1, lpSurfaceLocal, dwPitchAlignment, lpSurfaceDesc, 0);
        }
        else if (dwSurfCaps & DDSCAPS_SYSTEMMEMORY)
        {
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("Create FOURCC/RGB Offscreen surface in System Memory.\r\n")));

            /* allocate the surface data */
            SystemSurfaceAllocate( (DDGPESurf **)&lpSurfaceLocal->dwReserved1, lpSurfaceLocal, lpSurfaceDesc);
        }
        /* if neither is defined, try video memory */
        else if ((dwSurfCaps & (DDSCAPS_VIDEOMEMORY | DDSCAPS_SYSTEMMEMORY)) == 0)  
        {
            /* try video memory first; if it fails, try system memory*/

            DEBUGMSG(GPE_ZONE_HISI, (TEXT("FOURCC/RGB Offscreen Video Memory.\r\n")));

            if (bPrimary)
            {
                sc = pDDGPE->AllocBackBuffer(lpSurfaceLocal);
                    
                if (!SUCCEEDED(sc))
                {
                    goto CREATESURF_FAILED;      // we allocated the first option for the surface, break from the Retries loop
                }
            }
            else
            {

                /* allocate the surface data */
                SurfaceAllocate((DDGPESurf **)&lpSurfaceLocal->dwReserved1, lpSurfaceLocal, dwPitchAlignment, lpSurfaceDesc, 0);
                if (lpSurfaceLocal->dwReserved1 == (ULONG_PTR) NULL)
                {
                    /* try system memory now... */
                    DEBUGMSG(GPE_ZONE_HISI, (TEXT("FOURCC/RGB Offscreen System Memory.\r\n")));

                    /* allocate the surface data */
                    SystemSurfaceAllocate( (DDGPESurf **)&lpSurfaceLocal->dwReserved1, lpSurfaceLocal, lpSurfaceDesc);
                }
            }
        }
        else
        {
            DEBUGMSG(GPE_ZONE_HISI, (TEXT(
                "HalCreateSurface: Unknown Surface type.\r\n")));
            DEBUGLEAVE( HalCreateSurface );
            return (DDHAL_DRIVER_NOTHANDLED);
        }

        if (lpSurfaceLocal->dwReserved1 == (ULONG_PTR)NULL)
        {
            goto CREATESURF_FAILED;
        }

        DEBUGMSG(GPE_ZONE_HISI, (TEXT(
                 "HalCreateSurface: Created Surface, ptr = %p with Pitch = %d.\r\n"),
                 lpSurfaceLocal,
                 (DWORD) lpSurfaceDesc->lPitch
                 ));
#if 1
        if (dwSurfCaps & DDSCAPS_OVERLAY) {

            // Overlays must have the same rotation flag as the primary.

            DDGPESurf * pPrimary = pDDGPE->DDGPEPrimarySurface();
            DDGPESurf * pOverlay = DDGPESurf::GetDDGPESurf(lpSurfaceLocal);

            pOverlay->SetOverlay();
            pOverlay->SetRotation(pOverlay->Width(),
                                  pOverlay->Height(),
                                  pPrimary->Rotate());
        }
#endif
/*
        lpSurfaceDesc->lPitch   = ((DDGPESurf *)lpSurfaceLocal->dwReserved1)->Stride();
        lpSurfaceDesc->dwFlags |= DDSD_PITCH;
*/
        if (DDGPESurf::GetDDGPESurf(lpSurfaceLocal)->InVideoMemory()) 
        {
            lpSurfaceLocal->ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
        }
        else 
        {
            lpSurfaceLocal->ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
        }
    }

     /* 
      * Reach back into the surface description to fill out the surface stride.
      *  (All surfaces in a flipping chain should have the same stride.)
      */            

    lpSurfaceLocal = lpCreateSurface->lplpSList[0];
    if (lpSurfaceLocal)
    {
        int nStrideBytes = DDGPESurf::GetDDGPESurf(lpSurfaceLocal)->Stride();
        int BytesPerPixel = DDGPESurf::GetDDGPESurf(lpSurfaceLocal)->BytesPerPixel();
        int Rotate = DDGPESurf::GetDDGPESurf(lpSurfaceLocal)->Rotate();

        switch (Rotate)
        {
        case DMDO_0:
            lpCreateSurface->lpDDSurfaceDesc->lPitch = nStrideBytes;
            lpCreateSurface->lpDDSurfaceDesc->lXPitch = BytesPerPixel;
            break;

        case DMDO_90:
            lpCreateSurface->lpDDSurfaceDesc->lPitch = BytesPerPixel;
            lpCreateSurface->lpDDSurfaceDesc->lXPitch = -nStrideBytes;
            break;

        case DMDO_180:
            lpCreateSurface->lpDDSurfaceDesc->lPitch = -nStrideBytes;
            lpCreateSurface->lpDDSurfaceDesc->lXPitch = -BytesPerPixel;
            break;

        case DMDO_270:
            lpCreateSurface->lpDDSurfaceDesc->lPitch = -BytesPerPixel;
            lpCreateSurface->lpDDSurfaceDesc->lXPitch = nStrideBytes;
            break;
        }

        //lpCreateSurface->lpDDSurfaceDesc->dwSurfaceSize = DDGPESurf::GetDDGPESurf(lpSurfaceLocal)->SurfaceSize();
        lpCreateSurface->lpDDSurfaceDesc->dwFlags |= (DDSD_PITCH | DDSD_XPITCH | DDSD_SURFACESIZE);
        if (DDGPESurf::GetDDGPESurf(lpSurfaceLocal)->InVideoMemory()) {
            lpCreateSurface->lpDDSurfaceDesc->ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
        }
        else {
            lpCreateSurface->lpDDSurfaceDesc->ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
        }
    }
    
    DEBUGLEAVE( HalCreateSurface );

    lpCreateSurface->ddRVal = DD_OK;

    return (DDHAL_DRIVER_HANDLED);

CREATESURF_FAILED:
    /* if i > 0, this means that there are surfaces allocate ok.  Must free them. */
    DEBUGMSG(GPE_ZONE_HISI, (TEXT("Create surface failed, free allocated ones.\r\n")));
    
    if(i > 0)
    {
        int j;
        for(j = i - 1; j >= 0; j--)
        {
            lpSurfaceLocal = lpCreateSurface->lplpSList[j];
            if(lpSurfaceLocal->dwReserved1)
            {
                if (!(lpSurfaceLocal->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE))
                    delete (HisiSurf *)lpSurfaceLocal->dwReserved1;

                lpSurfaceLocal->dwReserved1 = 0;
            }
        }
    }

    if (dwSurfCaps & DDSCAPS_VIDEOMEMORY)
    {
        lpCreateSurface->ddRVal = DDERR_OUTOFVIDEOMEMORY;
        DEBUGMSG(GPE_ZONE_HISI, (TEXT( " HalCreateSurface - Leave (DDERR_OUTOFVIDEOMEMORY).\r\n")));
        DEBUGLEAVE(HalCreateSurface);
        return ( DDHAL_DRIVER_HANDLED );
    }
    else
    {
        /* if system memory, try ddgpe layer */
        return DDGPECreateSurface(lpCreateSurface);
    }

CREATESURF_FAILTOOBIG:
    if (dwSurfCaps & DDSCAPS_VIDEOMEMORY)
    {
        DEBUGMSG(GPE_ZONE_HISI, (TEXT( "HalCreateSurface - Leave (DDERR_TOOBIGSIZE).\r\n")));
        lpCreateSurface->ddRVal = DDERR_TOOBIGSIZE;
        DEBUGLEAVE(HalCreateSurface);
        return (DDHAL_DRIVER_HANDLED);
    }
    else
    {
        /* if system memory, try ddgpe layer */
        return DDGPECreateSurface(lpCreateSurface);
    }
}

/*****************************************************
 * Function: HalCanCreateSurface
 * Description: indicates whether a surface can be created.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: pd - Pointer to a DDHAL_CANCREATESURFACEDATA 
 *             structure that the driver uses to determine 
 *             whether a surface can be created
 * Output: NONE
 * Return: NONE
 * Others: NONE
 ****************************************************/
DWORD HalCanCreateSurface(LPDDHAL_CANCREATESURFACEDATA lpCanCreateSurface)
{
    DWORD           dwPixelFormat;
    DWORD           dwPixelDepth;
    LPDDSURFACEDESC lpSurfaceDesc;
    DDSCAPS         SurfaceType;

    DEBUGENTER( HalCanCreateSurface );

    DWORD ret = DDHAL_DRIVER_HANDLED;

    lpSurfaceDesc = lpCanCreateSurface->lpDDSurfaceDesc;
    SurfaceType   = lpSurfaceDesc->ddsCaps;

    if ((lpSurfaceDesc->dwFlags & DDSD_PIXELFORMAT))
    {
        dwPixelFormat   = lpSurfaceDesc->ddpfPixelFormat.dwFlags;
        dwPixelDepth    = lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount;

        DEBUGMSG(GPE_ZONE_HISI, (TEXT(
                 "DdCanCreateSurface: Requested Pixelformat: %d x %d, %s %d Bpp.\r\n"),
                 (DWORD) lpSurfaceDesc->dwWidth,
                 (DWORD) lpSurfaceDesc->dwHeight,
                 (dwPixelFormat & DDPF_RGB) ? "RGB" : "FOURCC",
                 dwPixelDepth
            ));
    }
    else
    {
        /* get the surface values of the primary */

        dwPixelFormat = DDPF_RGB;
        dwPixelDepth  = ((HisiDisp *)GetGPE())->DDGPEPrimarySurface()->Bpp();
    }

    /* Default error if we don't support the given type */

    lpCanCreateSurface->ddRVal = DDERR_UNSUPPORTEDFORMAT;

    if ((SurfaceType.dwCaps & DDSCAPS_PRIMARYSURFACE) &&
        (dwPixelFormat & DDPF_RGB))
    {
        /* primary surface "creation" */
        DEBUGMSG(GPE_ZONE_HISI, (TEXT(
                 "CanCreateSurface: primary video memory surface, %d x %d, %d Bpp.\r\n"),
                 (DWORD) ((HisiDisp *)GetGPE())->DDGPEPrimarySurface()->Width(),
                 (DWORD) ((HisiDisp *)GetGPE())->DDGPEPrimarySurface()->Height(),
                 dwPixelDepth));

        lpCanCreateSurface->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;
    }
    else if (SurfaceType.dwCaps & DDSCAPS_OVERLAY)
    {
        DEBUGMSG(GPE_ZONE_HISI, (TEXT(
                 "CCS SurfaceType is Overlay, PixelDepth = %d.\r\n"),
                 dwPixelDepth));

        /* We want to create an overlay */

        if (!lpCanCreateSurface->bIsDifferentPixelFormat)
        {
            /*  Surface is of the same format as primary */

            /* RGB555/565/888 are supported for overlays */
            DWORD nBpp = ((HisiDisp *)GetGPE())->m_ModeInfoEx.modeInfo.Bpp;
            
            if ((16 == nBpp) || (24 == nBpp))
                lpCanCreateSurface->ddRVal = DD_OK;
            else
                lpCanCreateSurface->ddRVal = DDERR_UNSUPPORTEDFORMAT;
        }

        if (ValidSurfaceData(&lpSurfaceDesc->ddpfPixelFormat,
                                     TRUE))
        {
            lpCanCreateSurface->ddRVal = DD_OK;
        }
        else if (!(lpSurfaceDesc->ddpfPixelFormat.dwFlags & (DDPF_FOURCC | DDPF_RGB)))
        {
            /* It is okay to create an overlay without specifying a format.
               The primary's format will be used. */
            lpCanCreateSurface->ddRVal = DD_OK;
        }
        else
        {
            lpCanCreateSurface->ddRVal = DDERR_UNSUPPORTEDFORMAT;
        }
        return DDHAL_DRIVER_HANDLED;
    }
    else if ((lpCanCreateSurface->bIsDifferentPixelFormat) &&
             (dwPixelFormat & DDPF_FOURCC) &&
             !(SurfaceType.dwCaps & DDSCAPS_PRIMARYSURFACE) )
    {
        /* We want to be able to create a plain FourCC surface
         * for offscreen storage */

        DEBUGMSG(GPE_ZONE_HISI, (TEXT(
                 "CanCreateSurface: FourCC surface creation.\r\n")));

        if (!ValidSurfaceData(&lpSurfaceDesc->ddpfPixelFormat,
                                FALSE))
        {
            lpCanCreateSurface->ddRVal = DDERR_UNSUPPORTEDFORMAT;
            goto TryDDGPE;
        }
        else
        {
            lpCanCreateSurface->ddRVal = DD_OK;
        }
        return DDHAL_DRIVER_HANDLED;
    }
    else if (!(SurfaceType.dwCaps & DDSCAPS_PRIMARYSURFACE))
    {
        /* simple offscreen buffer surface creation */
        DEBUGMSG(GPE_ZONE_HISI, (TEXT( "CanCreateSurface: generic video memory surface.\r\n")));

        if (!lpCanCreateSurface->bIsDifferentPixelFormat ||
            ValidSurfaceData(&lpSurfaceDesc->ddpfPixelFormat,
                               FALSE))
        {
            /*
             * Surface is of the same format as primary (supported by default)
             * or belongs to the supported formatse
             */

            lpCanCreateSurface->ddRVal = DD_OK;
        }
        else 
        {
            goto TryDDGPE;
        }
        return DDHAL_DRIVER_HANDLED;
    }

    ret = DDHAL_DRIVER_NOTHANDLED;

TryDDGPE:
    if (SurfaceType.dwCaps & DDSCAPS_VIDEOMEMORY)
    {
        return ret;
    }
    else
    {
        /* for system memory surfaces, see if our emulation can deal with it... */
        return DDGPECanCreateSurface(lpCanCreateSurface);
    }
}

/******************************Public*Routine******************************
 * DWORD HalDestroySurface
 *
 **************************************************************************/

/*****************************************************
 * Function: HalDestroySurface
 * Description: destroy a specified surface.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: pd - Pointer to a DDHAL_DESTROYSURFACEDATA 
 *             structure that the driver uses to destroy 
 *             the specified surface.
 * Output: NONE
 * Return: NONE
 * Others: NONE
 ****************************************************/
//extern char pEDCBaseAddress_phy[512];
DWORD HalDestroySurface(LPDDHAL_DESTROYSURFACEDATA lpDestroySurface)
{
    HisiSurf *lpHisiSurf;

    DEBUGENTER(HalDestroySurface);

    lpHisiSurf = (HisiSurf *) lpDestroySurface->lpDDSurface->dwReserved1;

    lpDestroySurface->lpDDSurface->dwReserved1 = 0;         // safety measure

    if (lpDestroySurface->lpDDSurface->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
    {
        DEBUGMSG(GPE_ZONE_HISI, (TEXT( "HalDestroySurface: free up Primary screen")));
    }

    if (lpHisiSurf != NULL)
    {
        DEBUGMSG(GPE_ZONE_HISI, (TEXT(
                 "HalDestroySurface: DDGPESurf ptr = %lx, size %d x %d, Caps = %lx"),
                 lpHisiSurf,
                 (LONG)lpHisiSurf->Width(), (LONG)lpHisiSurf->Height(),
                 lpDestroySurface->lpDDSurface->ddsCaps.dwCaps));

        /* we don't want to release the Primary surface here,
           because it was allocated through GDI's DrvSurfaceEnable callback */

        if (!(lpDestroySurface->lpDDSurface->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE))
        {
            /* BEGIN: Added by leiyu, 2008/02/28   PN:AT4D01049 */
            //memset(lpHisiSurf->Buffer(), 0, lpHisiSurf->SurfaceSize());
            /* END:   Added by leiyu, 2008/02/28 */
            
            /* the destructor for this class frees the allocation */
            delete lpHisiSurf;
        }
    }
    else
    {
        /* Someone else had allocated the surface */
        DEBUGLEAVE(HalDestroySurface);
        return (DDHAL_DRIVER_NOTHANDLED);
    }

    lpDestroySurface->ddRVal = DD_OK;
    /*
    DEBUGMSG(GPE_ZONE_HISI, (TEXT( "HalDestroySurface:GE registers%08x"),
        *((DWORD*)&pEDCBaseAddress_phy[EDC_GRP_ADDR])
        ));
        */

    DEBUGLEAVE(HalDestroySurface);
    return (DDHAL_DRIVER_HANDLED);
}

/*****************************************************
 * Function: GetFormat
 * Description: get pixel format according to surface 
 *              description.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: lpDDSurfaceDesc - surface description 
 * Output: pPixelFormat - DDGPE pixel format
 *         pFormat - GPE pixel format 
 * Return: NONE
 * Others: NONE
 ****************************************************/
static void GetFormat(LPDDSURFACEDESC lpDDSurfaceDesc,
                         EDDGPEPixelFormat *pPixelFormat,
                         EGPEFormat *pFormat)
{
    /* Most formats are processed by DDGPEGetPixelFormatFromSurfaceDesc */
    if (S_OK != DDGPEGetPixelFormatFromSurfaceDesc(lpDDSurfaceDesc,
                                                   pPixelFormat,
                                                   pFormat))
    {
        /* we can add some format not supported above */
        if (lpDDSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_FOURCC) 
        {
            *pFormat = gpe16Bpp;
            switch (lpDDSurfaceDesc->ddpfPixelFormat.dwFourCC)
            {
            case FOURCC_YVYU:
                *pPixelFormat = ddgpePixelFormat_YUYV422;
                break;
            
            case FOURCC_YUY2:
                *pPixelFormat = ddgpePixelFormat_YUY2422;
                break;
        
            case FOURCC_UYVY:
                *pPixelFormat = ddgpePixelFormat_UYVY422;
                break;
            case FOURCC_NV12:
                *pPixelFormat = ddgpePixelFormat_CustomFormat;
                break;    
            default:
                DEBUGMSG(GPE_ZONE_HISI, (TEXT("FOURCC %d not supported"),
                                lpDDSurfaceDesc->ddpfPixelFormat.dwFourCC));
                break;
            }
        }
        else
        {
            /* we do nothing to RGB format, so only 16/32 bpp are supported */
#if 0  
            switch(lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount)
            {
            case 1:
                *pFormat      = gpe1Bpp;
                *pPixelFormat = ddgpePixelFormat_1bpp;
                break;
            case 2:
                *pFormat      = gpe2Bpp;
                *pPixelFormat = ddgpePixelFormat_2bpp;
                break;
            case 4:
                *pFormat      = gpe4Bpp;
                *pPixelFormat = ddgpePixelFormat_4bpp;
                break;
            case 8:
                *pFormat      = gpe8Bpp;
                *pPixelFormat = ddgpePixelFormat_8bpp;
                break;
            case 24:
                *pFormat      = gpe24Bpp;
                *pPixelFormat = ddgpePixelFormat_8880;
                break;                
               
            case 16:
                *pFormat      = gpe16Bpp;                
                *pPixelFormat = ddgpePixelFormat_565;
                break;
                
            case 32:
                *pFormat      = gpe32Bpp;
                *pPixelFormat = ddgpePixelFormat_8880;
                break;
                 
            default:
                DEBUGMSG(GPE_ZONE_HISI, (TEXT("No such format!!!\n")));
                break;
            }
#endif            
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("No such format!!!\n")));
        }
    }
}

/*****************************************************
 * Function: SystemSurfaceAllocate
 * Description: Allocates the surface in system memory  
 *              and initialises parameters.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input:  
 * Output: 
 * Return: TRUE: success FALSE: failure
 * Others: NONE
 ****************************************************/
static BOOL SystemSurfaceAllocate (DDGPESurf                ** ppGPESurf,
                                        DDRAWI_DDRAWSURFACE_LCL   * lpLcl,
                                        LPDDSURFACEDESC             lpDDSurfaceDesc)
{
    DDGPESurf          *lpHisiSurf;
    DWORD               dwWidth;
    DWORD               dwHeight;
    EDDGPEPixelFormat   pixelFormat;    // Pixel format of surface(s) being created
    EGPEFormat          format;

    GetFormat (lpDDSurfaceDesc, &pixelFormat, &format);

    dwWidth     = lpDDSurfaceDesc->dwWidth;
    dwHeight    = lpDDSurfaceDesc->dwHeight;

    if (S_OK != ((HisiDisp *)GetGPE())->AllocSurface(&lpHisiSurf,
                                                dwWidth,
                                                dwHeight,
                                                format,
                                                pixelFormat,
                                                0))
    {
        return FALSE;
    }

    *ppGPESurf = lpHisiSurf;

    lpDDSurfaceDesc->lPitch = lpHisiSurf->Stride();
    lpDDSurfaceDesc->dwSurfaceSize = lpHisiSurf->SurfaceSize();
    lpDDSurfaceDesc->dwFlags |= (DDSD_PITCH | DDSD_SURFACESIZE);

    return TRUE;
}

/*****************************************************
 * Function: SurfaceAllocate
 * Description: Allocates the surface in video memory  
 *              and initialises parameters.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: 
 * Output: 
 * Return: TRUE: success FALSE: failure
 * Others: NONE
 ****************************************************/
static BOOL SurfaceAllocate (DDGPESurf                ** ppGPESurf,
                                DDRAWI_DDRAWSURFACE_LCL   * lpLcl,
                                DWORD                       dwPitchOverride,
                                LPDDSURFACEDESC             lpDDSurfaceDesc,
                                DWORD                       dwSizeOverride)
{
    DDGPESurf          *lpHisiSurf;
    DWORD               dwWidth;
    DWORD               dwHeight;
    EDDGPEPixelFormat   pixelFormat;    // Pixel format of surface(s) being created
    EGPEFormat          format;
    DDGPE *              pDDGPE = GetDDGPE();

    dwWidth  = lpDDSurfaceDesc->dwWidth;
    dwHeight = lpDDSurfaceDesc->dwHeight;


    /* BEGIN: Added by leiyu, 2008/02/26   PN:AT4D01035 */
    if (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_OVERLAY)
    {
        switch (pDDGPE->DDGPEPrimarySurface()->Rotate())
        {
        case DMDO_90:
        case DMDO_270:
            dwWidth = lpDDSurfaceDesc->dwHeight;
            dwHeight = lpDDSurfaceDesc->dwWidth;
            break;
        default:
            break;
        }
    }
    /* END:   Added by leiyu, 2008/02/26 */


    GetFormat(lpDDSurfaceDesc, &pixelFormat, &format);

    if (S_OK != ((HisiDisp *)GetGPE())->AllocSurface(&lpHisiSurf,
                                                dwWidth,
                                                dwHeight,
                                                dwPitchOverride,
                                                dwSizeOverride,
                                                format,
                                                pixelFormat))
    {
        return FALSE;
    }

    /* pass surface back to caller */
    *ppGPESurf = lpHisiSurf;     

    lpDDSurfaceDesc->lPitch = lpHisiSurf->Stride();
    lpDDSurfaceDesc->dwSurfaceSize = lpHisiSurf->SurfaceSize();
    lpDDSurfaceDesc->dwFlags |= (DDSD_PITCH | DDSD_SURFACESIZE);

    return TRUE;
}

/*****************************************************
 * Function: CreateVideoSurface
 * Description: check for specific video/overlay parameters,
 *              allocate a surface and fill in all the 
 *              necessary information.
 * Calls: 
 * Called by: 
 * Table Access: list the table you will use. 
 * Table Update: list the table which you will update.
 * Input: dwPitchOverride - stride to override
 * Output: ppGPESurf - pointer to DDGPESurf
 * Return: NONE
 * Others: NONE
 ****************************************************/
void CreateVideoSurface(DDGPESurf                ** ppGPESurf,
                             DDRAWI_DDRAWSURFACE_LCL   * lpLcl,
                             LPDDSURFACEDESC             lpSurfaceDesc,
                             DWORD                       dwPitchOverride,
                             BOOL                        blOverlayUse)
{
    DWORD dwWidth  = (DWORD) lpSurfaceDesc->dwWidth;
    DWORD dwHeight = (DWORD) lpSurfaceDesc->dwHeight;

    DWORD dwYPitch, dwUVPitch;
    DWORD dwUVHeight, dwYHeight;
    DWORD dwSurfaceSize;
    
    DDGPE * pDDGPE = GetDDGPE();


    /* BEGIN: Added by leiyu, 2008/02/26   PN:AT4D01035 */
    if (lpSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_OVERLAY)
    {
        switch (pDDGPE->DDGPEPrimarySurface()->Rotate())
        {
        case DMDO_90:
        case DMDO_270:
            dwWidth = lpSurfaceDesc->dwHeight;
            dwHeight = lpSurfaceDesc->dwWidth;
            break;
        default:
            break;
        }
    }
    /* END:   Added by leiyu, 2008/02/26 */

    /*
     * See what type of surface the client is requesting.
     * This function validates the surface type and sets up
     * the pixel format data for the FOURCC surface
     */

    if ((FALSE == ValidSurfaceData(&lpSurfaceDesc->ddpfPixelFormat,
                                    blOverlayUse))
        && (blOverlayUse && (lpSurfaceDesc->ddpfPixelFormat.dwFlags & (DDPF_FOURCC | DDPF_RGB))))
    {
        DEBUGMSG(GPE_ZONE_HISI, (TEXT("Format of surface client is requesting is not supported.\r\n")));
        return;
    }

    if (lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_FOURCC)
    {
        switch (lpSurfaceDesc->ddpfPixelFormat.dwFourCC)
        {
        case FOURCC_YVYU:
        case FOURCC_YUY2:
        case FOURCC_UYVY:  
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("%d FOURCC video surface is supported.\n"), 
                            lpSurfaceDesc->ddpfPixelFormat.dwFourCC));
            
            if (!SurfaceAllocate(ppGPESurf, lpLcl, dwPitchOverride, lpSurfaceDesc, 0))
            {
                return;
            }
            break;

        case FOURCC_NV12:
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("%d FOURCC video surface is supported.\n"), 
                            lpSurfaceDesc->ddpfPixelFormat.dwFourCC));

            dwYPitch = dwUVPitch = AlignTo(dwWidth, 8);
            
            dwYHeight = AlignTo(dwHeight, 8);
            dwUVHeight = dwYHeight / 2;

            dwSurfaceSize = dwYPitch * dwYHeight + dwUVPitch * dwUVHeight;

            if (!SurfaceAllocate(ppGPESurf,
                     lpLcl,
                     dwYPitch,
                     lpSurfaceDesc,
                     dwSurfaceSize))
            {
                return;
            }
            break;
 
        case FOURCC_YV12:
            /*
             * Planar format...
             * We have to align the pitch according to the requirements
             * of the hardware.
             * V/U: 4 bytes alignment
             * Y: 8 bytes alignment
             */

            DEBUGMSG(GPE_ZONE_HISI, (TEXT("%d FOURCC video surface is supported.\n"), 
                            lpSurfaceDesc->ddpfPixelFormat.dwFourCC));
            
            dwUVPitch = AlignTo(dwWidth, 2) / 2;
            dwUVPitch = AlignTo(dwUVPitch, 4);

            dwYPitch  = dwUVPitch * 2;

            dwYHeight = AlignTo(dwHeight, 2);
            dwUVHeight = dwYHeight / 2;

            dwSurfaceSize = dwYPitch * dwYHeight + 2 * (dwUVPitch * dwUVHeight);

            if (!SurfaceAllocate(ppGPESurf,
                                 lpLcl,
                                 dwYPitch,
                                 lpSurfaceDesc,
                                 dwSurfaceSize))
            {
                return;
            }
            break;

        default:
            /* nothing's allocated */
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("%d FOURCC video surface not supported.\n"), 
                            lpSurfaceDesc->ddpfPixelFormat.dwFourCC));
            return;
        }

        DEBUGMSG(GPE_ZONE_CREATE, (TEXT( "Created FOURCC %s: %li x %li"),
                 &lpSurfaceDesc->ddpfPixelFormat.dwFourCC,
                 lpSurfaceDesc->dwWidth, lpSurfaceDesc->dwHeight));
    }
    else
    {
        DWORD dwRGBBitCount;

        if ((lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB))
        {
            /* RGB surface format is not the same as the primary screen */

            dwRGBBitCount = lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount;
        }
        else
        {
            /* RGB surface format is the same as the primary screen */

            dwRGBBitCount = ((HisiDisp *)GetGPE())->m_ModeInfoEx.modeInfo.Bpp;
        }

        /* What is pixel depth of surface? */

        switch (dwRGBBitCount)
        {
        case 16:
        case 32:
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("%d bpp video surface supported.\n"), dwRGBBitCount));
            
            if (!SurfaceAllocate(ppGPESurf, lpLcl, dwPitchOverride, lpSurfaceDesc, 0))
            {
                return;
            }
            break;

        default:
            /* nothing's allocated */
            DEBUGMSG(GPE_ZONE_HISI, (TEXT("%d bpp video surface not supported.\n"), dwRGBBitCount));
            return;
        }

        DEBUGMSG(GPE_ZONE_HISI, (TEXT( "Created RGB %libpp: %li x %li, Redmask %lx"),
                 lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
                 lpSurfaceDesc->dwWidth, lpSurfaceDesc->dwHeight,
                 lpSurfaceDesc->ddpfPixelFormat.dwRBitMask));
    }
}

#endif

